﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;

namespace Utils.Input
{
    public enum MOUSE_BUTTONS
    {
        LEFT = 0,
        RIGHT,
        MIDDLE,
        XBUTTON1,
        XBUTTON2,
        COUNT
    }

    public class InputManager
    {
        static InputManager defaultInstance;

        public const int INPUT_COUNT = 4;

        public static Buttons[] ALL_BUTTONS = { Buttons.A, Buttons.B, Buttons.Back, Buttons.BigButton,
                                                Buttons.DPadDown, Buttons.DPadLeft, Buttons.DPadRight, Buttons.DPadUp,
                                                Buttons.LeftShoulder, Buttons.LeftStick, Buttons.LeftThumbstickDown,
                                                Buttons.LeftThumbstickLeft, Buttons.LeftThumbstickRight, Buttons.LeftThumbstickUp,
                                                Buttons.LeftTrigger, Buttons.RightShoulder, Buttons.RightStick,
                                                Buttons.RightThumbstickDown, Buttons.RightThumbstickLeft, Buttons.RightThumbstickRight,
                                                Buttons.RightThumbstickUp, Buttons.RightTrigger, Buttons.Start, Buttons.X, Buttons.Y};

        public static Keys[] ALL_KEYS = {   Keys.A, Keys.Add, Keys.Apps, Keys.Attn, Keys.B, Keys.Back, Keys.BrowserBack, Keys.BrowserFavorites,
                                            Keys.BrowserForward, Keys.BrowserHome, Keys.BrowserRefresh, Keys.BrowserSearch, Keys.BrowserStop,
                                            Keys.C, Keys.CapsLock, Keys.ChatPadGreen, Keys.ChatPadOrange, Keys.Crsel, Keys.D, Keys.D0,
                                            Keys.D1, Keys.D2, Keys.D3, Keys.D4, Keys.D5, Keys.D6, Keys.D7, Keys.D8, Keys.D9, Keys.Decimal,
                                            Keys.Delete, Keys.Divide, Keys.Down, Keys.E, Keys.End, Keys.Enter, Keys.EraseEof, Keys.Escape,
                                            Keys.Execute, Keys.Exsel, Keys.F, Keys.F1, Keys.F10, Keys.F11, Keys.F12, Keys.F13, Keys.F14,
                                            Keys.F15, Keys.F16, Keys.F17, Keys.F18, Keys.F19, Keys.F2, Keys.F20, Keys.F21, Keys.F22, Keys.F23,
                                            Keys.F24, Keys.F3, Keys.F4, Keys.F5, Keys.F6, Keys.F7, Keys.F8, Keys.F9, Keys.G, Keys.H, Keys.Help,
                                            Keys.Home, Keys.I, Keys.ImeConvert, Keys.ImeNoConvert, Keys.Insert, Keys.J, Keys.K, Keys.Kana,
                                            Keys.Kanji, Keys.L, Keys.LaunchApplication1, Keys.LaunchApplication2, Keys.LaunchMail, Keys.Left,
                                            Keys.LeftAlt, Keys.LeftControl, Keys.LeftShift, Keys.LeftWindows, Keys.M, Keys.MediaNextTrack,
                                            Keys.MediaPlayPause, Keys.MediaPreviousTrack, Keys.MediaStop, Keys.Multiply, Keys.N, Keys.None,
                                            Keys.NumLock, Keys.NumPad0, Keys.NumPad1, Keys.NumPad2, Keys.NumPad3, Keys.NumPad4, Keys.NumPad5,
                                            Keys.NumPad6, Keys.NumPad7, Keys.NumPad8, Keys.NumPad9, Keys.O, Keys.Oem8, Keys.OemAuto,
                                            Keys.OemBackslash, Keys.OemClear, Keys.OemCloseBrackets, Keys.OemComma, Keys.OemCopy, Keys.OemEnlW,
                                            Keys.OemMinus, Keys.OemOpenBrackets, Keys.OemPeriod, Keys.OemPipe, Keys.OemPlus, Keys.OemQuestion,
                                            Keys.OemQuotes, Keys.OemSemicolon, Keys.OemTilde, Keys.P, Keys.Pa1, Keys.PageDown, Keys.PageUp,
                                            Keys.Pause, Keys.Play, Keys.Print, Keys.PrintScreen, Keys.ProcessKey, Keys.Q, Keys.R, Keys.Right,
                                            Keys.RightAlt, Keys.RightControl, Keys.RightShift, Keys.RightWindows, Keys.S, Keys.Scroll,
                                            Keys.Select, Keys.SelectMedia, Keys.Separator, Keys.Sleep, Keys.Space, Keys.Subtract, Keys.T,
                                            Keys.Tab, Keys.U, Keys.Up, Keys.V, Keys.VolumeDown, Keys.VolumeMute, Keys.VolumeUp, Keys.W, Keys.X,
                                            Keys.Y, Keys.Z, Keys.Zoom};

        KeyboardState[] currentKeyboardStates;
        GamePadState[] currentGamePadStates;
        MouseState currentMouseState;
        bool[] currentMouseButtonsState;

        KeyboardState[] lastKeyboardStates;
        GamePadState[] lastGamePadStates;
        MouseState lastMouseState;
        bool[] lastMouseButtonsState;

        public static InputManager DEFAULT {
            get 
            {
                if(defaultInstance == null) 
                {
                    defaultInstance = new InputManager();
                }
                return defaultInstance;
            }
        }

        public InputManager()
        {
            currentKeyboardStates = new KeyboardState[INPUT_COUNT];
            currentGamePadStates = new GamePadState[INPUT_COUNT];
            currentMouseButtonsState = new bool[(int)MOUSE_BUTTONS.COUNT];

            lastKeyboardStates = new KeyboardState[INPUT_COUNT];
            lastGamePadStates = new GamePadState[INPUT_COUNT];
            lastMouseButtonsState = new bool[(int)MOUSE_BUTTONS.COUNT];
        }

        public void Update()
        {
            // mouse input
            lastMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();
            for (int i = 0; i < (int)MOUSE_BUTTONS.COUNT; i++)
            {
                lastMouseButtonsState[i] = currentMouseButtonsState[i];
            }
            currentMouseButtonsState[(int)MOUSE_BUTTONS.LEFT] = currentMouseState.LeftButton == ButtonState.Pressed;
            currentMouseButtonsState[(int)MOUSE_BUTTONS.RIGHT] = currentMouseState.RightButton == ButtonState.Pressed;
            currentMouseButtonsState[(int)MOUSE_BUTTONS.MIDDLE] = currentMouseState.MiddleButton == ButtonState.Pressed;
            currentMouseButtonsState[(int)MOUSE_BUTTONS.XBUTTON1] = currentMouseState.XButton1 == ButtonState.Pressed;
            currentMouseButtonsState[(int)MOUSE_BUTTONS.XBUTTON2] = currentMouseState.XButton2 == ButtonState.Pressed;

            // keyboard + gamepad input
            for (int i = 0; i < INPUT_COUNT; i++)
            {
                lastKeyboardStates[i] = currentKeyboardStates[i];
                lastGamePadStates[i] = currentGamePadStates[i];

                currentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
                currentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
            }
        }

        /*
         * Mouse Iput
         */
        public bool IsButtonPressed(MOUSE_BUTTONS button)
        {
            return currentMouseButtonsState[(int)button] && !lastMouseButtonsState[(int)button];
        }

        public bool IsButtonReleased(MOUSE_BUTTONS button)
        {
            return !currentMouseButtonsState[(int)button] && lastMouseButtonsState[(int)button];
        }

        public bool IsButtonDown(MOUSE_BUTTONS button)
        {
            return currentMouseButtonsState[(int)button];
        }

        public Vector2 GetMousePosition()
        {
            return new Vector2(currentMouseState.X, currentMouseState.Y);
        }

        /*
         * Gamepad Input
         */
        public bool IsButtonPressed(Buttons button, int gamePad)
        {
            if (gamePad >= 0 && gamePad <= INPUT_COUNT)
            {
                return currentGamePadStates[gamePad].IsButtonDown(button)
                        && !lastGamePadStates[gamePad].IsButtonDown(button);
            }
            else
            {
                bool result = false;
                for (int i = 0; i < INPUT_COUNT; i++)
                {
                    result |= IsButtonPressed(button, i);
                }
                return result;
            }
        }

        public bool IsButtonReleased(Buttons button, int gamePad)
        {
            if (gamePad >= 0 && gamePad <= INPUT_COUNT)
            {
                return !currentGamePadStates[gamePad].IsButtonDown(button)
                        && lastGamePadStates[gamePad].IsButtonDown(button);
            }
            else
            {
                bool result = false;
                for (int i = 0; i < INPUT_COUNT; i++)
                {
                    result |= IsButtonReleased(button, i);
                }
                return result;
            }
        }

        public bool IsButtonDown(Buttons button, int gamePad)
        {
            if (gamePad >= 0 && gamePad <= INPUT_COUNT)
            {
                return currentGamePadStates[gamePad].IsButtonDown(button);
            }
            else
            {
                bool result = false;
                for(int i=0; i<INPUT_COUNT; i++)
                {
                    result |= IsButtonDown(button, i);
                }
                return result;
            }
        }

        public Vector2 GetLeftThumbStick(int gamePad)
        {
            return currentGamePadStates[gamePad].ThumbSticks.Left;
        }

        public Vector2 GetRightThumbStick(int gamePad)
        {
            return currentGamePadStates[gamePad].ThumbSticks.Right;
        }

        /*
         * Keyboard Input
         */
        public bool IsKeyPressed(Keys key, int keyboard)
        {
            if (keyboard >= 0 && keyboard <= INPUT_COUNT)
            {
                return currentKeyboardStates[keyboard].IsKeyDown(key)
                        && !lastKeyboardStates[keyboard].IsKeyDown(key);
            }
            else
            {
                bool result = false;
                for(int i=0; i<INPUT_COUNT; i++)
                {
                    result |= IsKeyPressed(key, i);
                }
                return result;
            }
        }

        public bool IsKeyReleased(Keys key, int keyboard)
        {
            if (keyboard >= 0 && keyboard <= INPUT_COUNT)
            {
                return !currentKeyboardStates[keyboard].IsKeyDown(key)
                        && lastKeyboardStates[keyboard].IsKeyDown(key);
            }
            else
            {
                bool result = false;
                for (int i = 0; i < INPUT_COUNT; i++)
                {
                    result |= IsKeyReleased(key, i);
                }
                return result;
            }
        }

        public bool IsKeyDown(Keys key, int keyboard)
        {
            if (keyboard >= 0 && keyboard <= INPUT_COUNT)
            {
                return currentKeyboardStates[keyboard].IsKeyDown(key);
            }
            else
            {
                bool result = false;
                for (int i = 0; i < INPUT_COUNT; i++)
                {
                    result |= IsKeyDown(key, i);
                }
                return result;
            }
        }
    }
}
